package cn.wenzy.aurora.framework.exception;

import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.util.StrUtil;
import cn.wenzy.aurora.framework.constant.IConstant;
import cn.wenzy.aurora.framework.response.ResponseCode;
import cn.wenzy.aurora.framework.response.ResponseResult;
import cn.wenzy.aurora.framework.support.i18n.I18nSupport;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.event.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler implements IConstant {


    @Autowired
    private I18nSupport i18n;

    /**
     * 处理通用异常 CommonException
     * @param ex {@link CommonException}
     * @return {@link ResponseResult}
     */
    @ExceptionHandler({CommonException.class})
    public Object handleCommonException(CommonException ex) {
        Level level = ex.getLevel();
        if (Level.WARN.equals(level)) {
            log.warn(ex.getMessage(), ex);
        }
        if (Level.ERROR.equals(level)) {
            log.error(ex.getMessage(), ex);
        }
        ResponseResult<Void> result = new ResponseResult<>();
        result.setCode(ResponseCode.FAIL);
        result.setMessage(ex.getMessage());
        return result;
    }

    @ExceptionHandler({MaxUploadSizeExceededException.class})
    public Object handleMaxUploadSizeExceededException(MaxUploadSizeExceededException ex) {
        String message = i18n.getMessage(I18N_MESSAGE.EXCEPTION.MAX_UPLOAD_SIZE_EXCEEDED, DataSizeUtil.format(ex.getMaxUploadSize()));
        log.error(message, ex);

        ResponseResult<Void> result = new ResponseResult<>();
        result.setCode(ResponseCode.FAIL);
        result.setMessage(message);
        return result;
    }

    /**
     * 校验 Bean信息 使用下面面两个都可以的，后面跟上一个BindResult，这样非常不方便，统一起来处理
     * {@link org.springframework.validation.annotation.Validated}
     * {@link jakarta.validation.Valid} spring mvc controller中
     * @param ex {@link MethodArgumentNotValidException}
     * @return {@link ResponseResult}
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public Object handleMethodArgumentNotValidException(Exception ex) {
        log.error(ex.getMessage(), ex);

        BindingResult bindResult = null;
        if (ex instanceof BindException) {
            bindResult = ((BindException) ex).getBindingResult();
        } else if (ex instanceof MethodArgumentNotValidException) {
            bindResult = ((MethodArgumentNotValidException) ex).getBindingResult();
        }
        String message = null;
        if (bindResult != null && bindResult.hasErrors()) {
            List<String> toSort = new ArrayList<>();
            for (ObjectError objectError : bindResult.getAllErrors()) {
                String defaultMessage = objectError.getDefaultMessage();
                toSort.add(defaultMessage);
            }
            toSort.sort(Comparator.reverseOrder());
            message = StrUtil.join(";", toSort);
        }

        ResponseResult<Void> result = new ResponseResult<>();
        result.setCode(ResponseCode.FAIL);
        result.setMessage(message);
        return result;
    }

    /**
     * 处理所有未知异常
     * @param ex {@link Exception}
     * @return {@link ResponseResult}
     */
    @ExceptionHandler({Exception.class})
    public Object handleException(Exception ex, HttpServletResponse response) {
        log.error(ex.getMessage(), ex);

        ResponseResult<Void> result = new ResponseResult<>();
        result.setCode(ResponseCode.FAIL);
        result.setMessage(i18n.getMessage(I18N_MESSAGE.EXCEPTION.UNKNOW_EXCEPTION));
        return result;
    }
}
